﻿using LightCAD.Core.Elements;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Core.Element3d
{
    public class LcSolid3d : LcElement, IElement3d
    {
        private Vector3 scales = new Vector3(1, 1, 1);
        private Quaternion quaternion = new Quaternion();
        public Vector3 InsertPoint = new Vector3();
        public Vector3 Position = new Vector3();
        public Euler Euler = new Euler(0, 0, 0, Euler.RotationOrders.XYZ);
        public Matrix4 Matrix = new Matrix4();
        public Solid3d Solid { get; protected set; }
        public LcMaterial[] Materials { get; set; }
        public LcMaterial[] EdgeMaterials { get; set; }
        public LcMaterial[] CurveMaterials { get; set; }
        public object Rt3DAction { get; set; }
        public LcSolid3d()
        {
            this.Type = BuiltinElementType.Solid3d;
            this.Euler.OnChange(() => this.quaternion.SetFromEuler(this.Euler));
        }

        private ListEx<Vector3> snap3dPoints = null;
        public ListEx<Vector3> Snap3dPoints
        {
            get
            {
                if (snap3dPoints == null)
                    snap3dPoints = GetSnap3dPoint();
                return snap3dPoints;
            }
        }
        public LcSolid3d ResetSnap3dPoints() 
        {
            this.snap3dPoints = null;
            return this;
        }

        protected virtual ListEx<Vector3> GetSnap3dPoint() 
        {
            //动态捕捉的点默认从边里面取
            var points = new ListEx<Vector3>();
            var posArray = this.Solid.Edge.Verteics;
            for (int i = 0; i < posArray.Length; i += 3)
            {
                points.PushNoRepeat(new Vector3(posArray[i], posArray[i + 1], posArray[i + 2]));
            }
            return points;
        }

        public virtual Box3 GetBoundingBox3d()
        {
            var box = new Box3();
            var vs = this.Solid.Geometry.Verteics;
            var tempV3 = ThreadContext.GetCurrThreadContext().Vector3;
            for (int i = 0; i < vs.Length; i+=3)
            {
                tempV3.Set(vs[i], vs[i + 1], vs[i + 2]);
                tempV3.ApplyMatrix4(this.Matrix);
                box.ExpandByPoint(tempV3);
            }
            return box;
        }
        public LcSolid3d UpdateMatrix() 
        {
            OnPropertyChangedBefore(nameof(Matrix), Matrix, Matrix);
            var orignMat= ThreadContext.GetCurrThreadContext().Matrix4Ctx._m1.MakeTranslation(-this.InsertPoint.X, -this.InsertPoint.Y, -this.InsertPoint.Z);
            this.Matrix.Compose(this.Position, quaternion, scales).Multiply(orignMat);
            ResetSnap3dPoints();
            OnPropertyChangedAfter(nameof(Matrix), Matrix, Matrix);
            return this;
        }
        public override LcElement Clone()
        {
            return this;//TODO..
        }
        public override void Copy(LcElement src)
        {
            base.Copy(src);
            
        }
        public virtual LcSolid3d PushOrPullFace(PlanarSurface3d plane, double dist)
        {
            if (this.Solid.Topoable && this.Solid.TopoFaceModel.Surfaces.Contains(plane))
            {
                //注意
                //往外拉的时候要注意会不会被其他面挡住
                //
                var planLoops = new ListEx<List<Curve3d>> { plane.OuterLoop };
                planLoops.AddRange(plane.InnerLoops);
                Dictionary<Surface3d, int> adjCurveIdx = new Dictionary<Surface3d, int>();
                var frontFace = new ListEx<Surface3d>();
                var backFace = new ListEx<Surface3d>();
                var aroundFace = new ListEx<Surface3d>();
                for (int i = 0; i < this.Solid.TopoFaceModel.Surfaces.Length; i++)
                {
                    var curFace = this.Solid.TopoFaceModel.Surfaces[i];
                    if (curFace == plane)
                        continue;
                    var curveIdx = curFace.OuterLoop.FindIndex(ol => planLoops.Any(pol =>pol.Any(polc=>CurveCoinCide(polc, ol))));
                    if (curveIdx >= 0)
                    {
                        var curve = curFace.OuterLoop[(curveIdx - 1 + curFace.OuterLoop.Count) % curFace.OuterLoop.Count];
                        if ((curve.Start - plane.Plane.CoplanarPoint(new Vector3())).Dot(plane.Plane.Normal) < 0)
                            backFace.Add(curFace);
                        else
                            frontFace.Add(curFace);
                        adjCurveIdx.Add(curFace, curveIdx);
                    }
                }
                //判断是否有面阻挡
                bool hasFaceStop = false;
                //拉起面
                if (dist > 0)
                {
                    for (int i = 0; i < frontFace.Length; i++)
                    {
                        var face = frontFace[i];
                        //平面直接用法量判断
                        if (face is PlanarSurface3d)
                        {
                            hasFaceStop = (face as PlanarSurface3d).Normal.AngleTo(plane.Normal) > Utils.HalfPI;
                            if (hasFaceStop)
                                break;
                            continue;
                        }
                        if (hasFaceStop)
                            break;
                        var curIdx = adjCurveIdx[face];
                        var adjCurve = face.OuterLoop[curIdx];
                        //TODO 其他类型面的阻挡判断
                        //foreach (var item in face.OuterLoop)
                        //{
                        //    if (item != adjCurve&&item.Start!=adjCurve.End)
                        //    {
                        //        var start = item.Start;
                        //        var prjPoint = plane.Plane.ProjectPoint(start);
                        //        prjPoint.

                        //    }
                        //}
                    }
                    

                }
                //推面
                else
                {
                }
                var faceOffset = plane.Normal.Clone().MulScalar(dist);
                if (!hasFaceStop)
                {
                    for (int i = 0; i < backFace.Length; i++)
                    {
                        var face = backFace[i];
                        var curIdx = adjCurveIdx[face];
                        var adjCurve = face.OuterLoop[curIdx];
                        if (face is PlanarSurface3d)
                        {
                            var preCurIdx = (face.OuterLoop.Count + curIdx - 1) % face.OuterLoop.Count;
                            var nextCurIdx = (face.OuterLoop.Count + curIdx + 1) % face.OuterLoop.Count;
                            var preCurve = face.OuterLoop[preCurIdx];
                            var nextCurve = face.OuterLoop[nextCurIdx];
                            if (preCurve is Line3d && nextCurve is Line3d)
                            {
                                var preLine = (Line3d)preCurve;
                                var nextLine = (Line3d)nextCurve;
                                var preDot = preLine.Dir.Dot(plane.Normal);
                                var nextDot = nextLine.Dir.Dot(plane.Normal);
                                if (Utils.IsEqual(Math.Abs(preDot), Math.Abs(nextDot)) && Utils.IsEqual(Math.Abs(preDot), 1))
                                {
                                   
                                    preLine.End= preLine.End.Clone().Add(faceOffset);
                                    nextLine.Start= nextLine.Start.Clone().Add(faceOffset);
                                    adjCurve = adjCurve.Clone();
                                    adjCurve.Translate(faceOffset);
                                    face.OuterLoop[curIdx] = adjCurve;
                                   
                                    continue;
                                }
                            }
                        }
                        if (adjCurve is Line3d)
                        {
                            var botLine = adjCurve.Clone() as Line3d;
                            var topLine = botLine.Clone().Reverse();
                            topLine.Translate(faceOffset);
                            var lefLine = new Line3d(topLine.End, botLine.Start);
                            var rightLine = new Line3d(botLine.End, topLine.Start);
                            var newNormal = botLine.Dir.Cross(plane.Normal);
                            var newPlane = new Plane().SetFromNormalAndCoplanarPoint(newNormal, botLine.Start);
                            var newFace = new PlanarSurface3d(newPlane, new List<Curve3d> { botLine, rightLine, topLine, lefLine });
                            this.Solid.TopoFaceModel.Surfaces.Add(newFace);
                        }
                        else if (adjCurve is Arc3d) 
                        {
                            var botCurve = adjCurve.Clone();
                            var topCurve = botCurve.Clone().Reverse();
                            topCurve.Translate(faceOffset);
                            var lefLine = new Line3d(topCurve.End, botCurve.Start);
                            var rightLine = new Line3d(botCurve.End, topCurve.Start);
                        }
                    }
                    for (int i = 0; i < plane.OuterLoop.Count; i++)
                    {
                        plane.OuterLoop[i] = plane.OuterLoop[i].Clone().Translate(faceOffset);
                    }
                    for (int i = 0; i < plane.InnerLoops.Count; i++)
                    {
                        for (int j = 0; j < plane.InnerLoops[i].Count; j++)
                        {
                            plane.InnerLoops[i][j] = plane.InnerLoops[i][j].Clone().Translate(faceOffset);
                        }
                    }
                    this.OnPropertyChangedAfter("TopoModel", this.Solid, this.Solid);
                }
            }
            return this;
        }

        protected  bool CurveCoinCide(Curve3d a,Curve3d b) 
        {
            if (a.Type == b.Type) 
            {
                if (a.Start == b.Start && a.End == b.End)
                    return true;
                if(a.Start==b.End &&a.End==b.Start)
                    return true;
            }
            return false;
        }
    }
}
